iT邦幫忙

2022 iThome 鐵人賽

DAY 7
0
Software Development

Kotlin on the way系列 第 7

Day 7 註解鬼故事 horrible story about comment

  • 分享至 

  • xImage
  •  

何謂註解?
下面解釋不含括 library 設計,我自己看過很多良好設計的 library 也會用註解詳加描述,但可信任的是註解總是隨著程式碼而更新

註解是用來描述程式碼失敗展示的意圖,對的失敗,如果程式碼能自己展現出意圖,就不需要註解來重複描述了,儘管語法有其極限,但我們仍應盡量以代碼表示

cat

註解的迷因有夠多快笑死

鬼故事合集

薛丁格的 export { }

我為了重現了我遇過的鬼故事,借用這個 sample 的代碼去修改,原始版本真的沒有這種作法

這樣的代碼,我是真實遇過的,原專案是一位開發者,寫了兩年的專案,大約 5~10 個檔案只有 export {}; 是沒被註解的,我也是笑笑,差點就笑不出來了


解法:用好你的 Git ,檔案該刪就刪,真要找去 commit 找就行

用神獸鎮壓鬼故事?

什麼?程式碼太鬼改不動? 請來佛祖鎮壓 bug !!

//
//                       _oo0oo_
//                      o8888888o
//                      88" . "88
//                      (| -_- |)
//                      0\  =  /0
//                    ___/`---'\___
//                  .' \\|     |// '.
//                 / \\|||  :  |||// \
//                / _||||| -:- |||||- \
//               |   | \\\  -  /// |   |
//               | \_|  ''\---/''  |_/ |
//               \  .-\__  '-'  ___/-. /
//             ___'. .'  /--.--\  `. .'___
//          ."" '<  `.___\_<|>_/___.' >' "".
//         | | :  `- \`.;`\ _ /`;.`/ - ` : | |
//         \  \ `_.   \_ __\ /__ _/   .-` /  /
//     =====`-.____`.___ \_____/___.-`___.-'=====
//                       `=---='
//
//
//     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//               佛祖保佑         永无BUG

轉自:https://gist.github.com/edokeh/7580064

除非團隊風氣真的如此,不然我覺得你要被炒了,這是在別人的專案裡好笑,在自己專案裡就笑不出來的東西


解法:真的去解 bug

網路迷因鬼故事

只有神知道的邏輯,沒成功的時數獻祭
only god knows


解法:先寫測試,然後對各部分做小規模重構,每次更動都要比前一次乾淨

一段糙 code

fun start(){
    val retrofit = Retrofit.Builder()
    .baseUrl("https://...")
    .build()
    
    val api = retrofit.create(Api::class.java)
    try{
        val data = api.getData()
        val article = Gson().fromJson(data, Article::class)
        findViewById(R.id.title).text = article.title
        findViewById(R.id.content).text = article.content
    } catch(e:Exception){
        
    }
}

data class Article{
    val title:String,
    val content:String
}

interface Api { 
    @GET("/")
    suspend fun getData():String
}

用上前面 6 天的技巧

抽取任務成 function

fun start(){
    
    when(getArticle()){
        Result.success -> {
            findViewById(R.id.title).text = article.title
        findViewById(R.id.content).text = article.content
        }
        Result.fail -> {
            //show error ui
        }
    }
}

fun provideRetrofit():Api{
    val retrofit = Retrofit.Builder()
    .baseUrl("https://...")
    .build()
    return retrofit.create(Api::class.java)
}

fun getArticle():Result<Article>{
    try {
        return Result.success(getArticleUseCase())
    } catch(e:Exception){
        return Result.fail(e)
    }
}
fun getArticleUseCase():Article{
    val data = provideRetrofit().getData()
    return Gson().fromJson(data, Article::class)
}

data class Article{
    val title:String,
    val content:String
}

interface Api { 
    @GET("/")
    suspend fun getData():String
}

抽象層級隔離

fun start(){
    
    when(getArticle()){
        Result.success -> {
            findViewById(R.id.title).text = article.title
        findViewById(R.id.content).text = article.content
        }
        Result.fail -> {
            //show error ui
        }
    }
}


//path src/di/provideRetrofit.kt
fun provideRetrofitApi():Api{
    val retrofit = Retrofit.Builder()
    .baseUrl("https://...")
    .build()
    return retrofit.create(Api::class.java)
}

//path src/usecase/GetArticle.kt
fun getArticle():Result<Article>{
    try {
        return Result.success(getArticleUseCase())
    } catch(e:Exception){
        return Result.fail(e)
    }
}
fun getArticleUseCase():Article{
    val data = provideRetrofit().getData()
    return Gson().fromJson(data, Article::class)
}

//path src/data/Article.ky
data class Article{
    val title:String,
    val content:String
}

// path src/api/Api.kt
interface Api { 
    @GET("/")
    suspend fun getData():String
}

現在這段程式碼根本不需要註解,因為我們已經為各層級添加了可讀性

  • data
    • /dto
      • Article.kt
  • usecase
    • /article
      • GetArticle.kt
  • di
    • /api
      • Api.kt
      • provideRetrofitApi.kt

這樣子權責分離的架構,可以讓閱讀者更好的理解專案,每個層級負責不同層次的任務,而這還沒導入更多 oop 的概念

可以接受的註解

好的註解,其實每一本程式碼品質的書都會詳細介紹
-《Clean Code》, Ch4 註解,整個章節都在介紹註解。
對於不好的註解的介紹有 68~81 頁之多
-《The Art of Readable Code》 ,Ch5 認識註解, Ch6 讓註解精確與簡潔。
花了兩個章節介紹
-《Code Complete 2/e》 ,Ch9 虛擬碼程式設計流程。
介紹如何寫完程式碼就擁有良好註解。

kdoc and dokka

每個語言都要自己的註解文件格式,像 kotlin 使用 kdoc ,且可以搭配 dokka 生成網頁專案文件,先了解團隊要求的文件範疇,必為其寫出好的文件

summary

註解會騙人,程式碼不會 程式碼不會就是不會
如果註解是為了描述程式意圖或流程,請試著讓程式自己表示
不要為糟糕的程式碼寫註解 - 重寫它

poorly code

English

What is comment?
The following explanation doesn't include library design, I had saw many good design library use comment, but a trustable comment should update with code

comment is used to describe code intention, yes fail
if the code can represent intention itself, and doesn;t need comment to redundancy describe, although the syntax has its limit, we should represent it by code

cat

horrible stories

Schrödinger's export { }

In order to recreate my experience, I modify the sample code, the original version of the project have great design

The project has empty export was maintain by one developer for two years


Solution, use your git as version control, delete file unnecessary, if you need it in future, you can find it in commit history

summon god to against bug?

What? you can't modify your code? bugs are everywhere? Summon budda to bless you

//
//                       _oo0oo_
//                      o8888888o
//                      88" . "88
//                      (| -_- |)
//                      0\  =  /0
//                    ___/`---'\___
//                  .' \\|     |// '.
//                 / \\|||  :  |||// \
//                / _||||| -:- |||||- \
//               |   | \\\  -  /// |   |
//               | \_|  ''\---/''  |_/ |
//               \  .-\__  '-'  ___/-. /
//             ___'. .'  /--.--\  `. .'___
//          ."" '<  `.___\_<|>_/___.' >' "".
//         | | :  `- \`.;`\ _ /`;.`/ - ` : | |
//         \  \ `_.   \_ __\ /__ _/   .-` /  /
//     =====`-.____`.___ \_____/___.-`___.-'=====
//                       `=---='
//
//
//     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//             Budda bless you, no more bug

reference:https://gist.github.com/edokeh/7580064

Unless your team culture is so, otherwise you might get fire, this only hilarious when it is not in your project


Solution : fixed your bug for real

meme story

Logic only god knows, count sacrificed time
only god knows


Solution: write test first, do micro refractory step by step, every refactor should make the file cleaner than before

Bad code sample

fun start(){
    val retrofit = Retrofit.Builder()
    .baseUrl("https://...")
    .build()
    
    val api = retrofit.create(Api::class.java)
    try{
        val data = api.getData()
        val article = Gson().fromJson(data, Article::class)
        findViewById(R.id.title).text = article.title
        findViewById(R.id.content).text = article.content
    } catch(e:Exception){
        
    }
}

data class Article{
    val title:String,
    val content:String
}

interface Api { 
    @GET("/")
    suspend fun getData():String
}

by using technique in the previous days

extract function

fun start(){
    
    when(getArticle()){
        Result.success -> {
            findViewById(R.id.title).text = article.title
        findViewById(R.id.content).text = article.content
        }
        Result.fail -> {
            //show error ui
        }
    }
}

fun provideRetrofit():Api{
    val retrofit = Retrofit.Builder()
    .baseUrl("https://...")
    .build()
    return retrofit.create(Api::class.java)
}

fun getArticle():Result<Article>{
    try {
        return Result.success(getArticleUseCase())
    } catch(e:Exception){
        return Result.fail(e)
    }
}
fun getArticleUseCase():Article{
    val data = provideRetrofit().getData()
    return Gson().fromJson(data, Article::class)
}

data class Article{
    val title:String,
    val content:String
}

interface Api { 
    @GET("/")
    suspend fun getData():String
}

separate abstract module

fun start(){
    
    when(getArticle()){
        Result.success -> {
            findViewById(R.id.title).text = article.title
        findViewById(R.id.content).text = article.content
        }
        Result.fail -> {
            //show error ui
        }
    }
}


//path src/di/provideRetrofit.kt
fun provideRetrofitApi():Api{
    val retrofit = Retrofit.Builder()
    .baseUrl("https://...")
    .build()
    return retrofit.create(Api::class.java)
}

//path src/usecase/GetArticle.kt
fun getArticle():Result<Article>{
    try {
        return Result.success(getArticleUseCase())
    } catch(e:Exception){
        return Result.fail(e)
    }
}
fun getArticleUseCase():Article{
    val data = provideRetrofit().getData()
    return Gson().fromJson(data, Article::class)
}

//path src/data/Article.ky
data class Article{
    val title:String,
    val content:String
}

// path src/api/Api.kt
interface Api { 
    @GET("/")
    suspend fun getData():String
}

Now the code doesn't need comment anymore, we add readability for each layer, and manage it by structure

  • data
    • /dto
      • Article.kt
  • usecase
    • /article
      • GetArticle.kt
  • di
    • /api
      • Api.kt
      • provideRetrofitApi.kt

with structure like this, reader can understand project well, and each layer in charge different task

Acceptable comment

Good comment is mentioned in every book discuss about code quality

-《Clean Code》, Ch4 comment
-《The Art of Readable Code》 ,Ch5 comment, Ch6 make comment precious and clean
-《Code Complete 2/e》 ,Ch9 design of pseudocode

kdoc and dokka

Each language has it owns comment document format, for example Kotlin use kdoc, and we can combine it with dokka to generate web doc

summary

Comment might lie to you, code is the only truth
If your comment describe code intention or process, you should let code represent itself
Don't write comment for bad code, you should rewrite it

poorly code

Reference:
不必要的註解
kdoc


上一篇
Day 6 函式職人,一生懸命 Make your function simple
下一篇
Day 8 Mutability 是把雙面刃 Mutability is double edged sword
系列文
Kotlin on the way31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言